using System;
using System.Collections;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Serialization;
using Awaiter;
using Minions.Classes;
using Nsb;
using Nsb.Classes;
using jQueryApi;

namespace MinionsEdit.Classes
{
    [IgnoreNamespace]
    public class MinionPaste
    {
        private static Dictionary<string, string> _reIds;
        private readonly MinionEdit _whereTo;
        private bool _noOffset; // Set true to prevent offsetting pastes to same page

        /// <summary>
        /// Initializes a new instance of the <see cref="MinionPaste"/> class.
        /// </summary>
        /// <param name="mm">The active minion</param>
        public MinionPaste(MinionEdit mm)
        {
            _whereTo = mm;
        }

        public static void Copy(MinionEdit mm)
        {
            MinionData md = mm.LiveData;
            List<MinionData> items = new List<MinionData>();
            items.Add(md);
            //ForCluster(e, delegate(GroupItem gi) { items.Add(gi.Gd); });
            Storage.SetLocal("CopyBuffer", items);
            Cluster.GlassOff();
            //RedrawPageGear();
        }

        /// <summary>
        /// Prevents offseting pasted object when it came from the same page.
        /// </summary>
        /// <returns></returns>
        public MinionPaste NoOffset()
        {
            _noOffset = true;
            return this;
        }

        /// <summary>
        /// Pastes from an event.
        /// </summary>
        /// <param name="e">The event.</param>
        public void PasteEv(jQueryEvent e)
        {
            Paste((List<MinionData>) Storage.GetLocal("CopyBuffer"));
        }

        [AlternateSignature]
        public extern void Paste(MinionData md);

        [AlternateSignature]
        public extern void Paste(string snapshot);

        public void Paste(List<MinionData> grplst)
        {
            if (grplst != null)
            {
                ReIdReset();

                if (Type.GetScriptType(grplst) == "string")
                    grplst = (List<MinionData>) Json.Parse((string) (object) grplst);

                if (!jQuery.IsArray(grplst))
                    grplst = (List<MinionData>) Script.Literal("[{0}]", grplst);

                Await awx = new Await();
                if (grplst.Count == 1)
                {
                    MinionData md = grplst[0];
                    md.groupId = null;
                    md.parentId = null;
                    md.clusterId = null;
                }
                foreach (MinionData item in grplst)
                {
                    PasteClosure(awx, item);
                }
                awx.AddDx(
                    delegate
                    {
                        Storage.SetLocal("CopyBuffer", grplst); // Update copy buffer with id changes
                        //ReSlideAndSave(e);
                        //jQuery.Select(PageCssTo).FadeIn(300); // Why? Where was it hidden before this?
                    });
                awx.Commit();
            }
        }

        private void PasteClosure(Await awp, MinionData item)
        {
            awp.AddDl(delegate(Await aw) { PasteAw(aw, item); });
        }

        /// <summary>
        /// Pastes an item to the active location.
        /// </summary>
        /// <param name="awp">The awp.</param>
        /// <param name="md">The MinionData.</param>
        public void PasteAw(Await awp, MinionData md)
        {
            if (!_noOffset && jQuery.Select("#" + md.id, _whereTo.AbsoluteParent().Element).Length > 0)
            {
                // If item came from page then shift it down before pasting
                md.pos.Top += 20;
            }
            md.id = ReId(md.id);
            if (md.parentId != null)
                md.parentId = ReId(md.parentId);
            if (md.groupId != null)
                md.groupId = ReId(md.groupId);
            if (md.clusterId != null)
                md.clusterId = ReId(md.clusterId);
            ReIdChildren(md);
            MinionEdit mm = (MinionEdit) _whereTo.NewMinion(_whereTo.TopMinion).SetData(md);
            CrossMetrics(_whereTo, mm);
            mm.AddTo(_whereTo).Paint();
            mm.ResizeFixedMetrics(_whereTo);
            MinionData tmd = _whereTo.Data;
            if (tmd.actions != null && tmd.actions.paste != null)
            {
                try
                {
                    Action<Minion, MinionData> fn = (Action<Minion, MinionData>) Script.Eval(tmd.actions.paste);
                    fn(mm, tmd);
                }
                catch (Exception ex)
                {
                    Inform.Error("Failed paste action: {0}", ex);
                }
            }
            awp.Done();
        }

        public static void CrossMetrics(Minion to, Minion mm)
        {
            if (mm.Data.relative && !to.Data.relative)
            {
                mm.Data.offsetParentWidth = to.Data.dim.width;
                mm.Data.offsetParentHeight = to.Data.dim.height;
            }
        }

        public static void ReIdReset()
        {
            _reIds = new Dictionary();
        }

        /// <summary>
        /// Generate a new id keeping track of id pairs already generated and reusing them so ids stay in sync
        /// </summary>
        /// <param name="id">The old id.</param>
        /// <returns>Single new id for the old id.</returns>
        public static string ReId(string id)
        {
            if (!Type.HasField(_reIds, id))
                _reIds[id] = Formulas.NewId;
            return _reIds[id];
        }

        public static void ReIdChildren(MinionData md)
        {
            List<string> ids = new List<string>();
            if (md.minions != null)
                foreach (KeyValuePair<string, MinionData> pair in md.minions)
                {
                    ids.Add(pair.Key);
                    pair.Value.id = ReId(pair.Value.id);
                    md.minions[pair.Value.id] = pair.Value;
                    ReIdChildren(pair.Value);
                }
            foreach (string id in ids)
            {
                Type.DeleteField(md.minions, id);
            }
        }
    }
}